#include <unistd.h>
#include <getopt.h>
#include <stdio.h>
#include <signal.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <sys/time.h>

#include "arguments.h"
#include "yhevent.h"
#include "yhsocket.h"
#include "daemon_init.h"

/* *
 * Author: Hong Yuan
 * COMM  : Nothing
 * */

void client_in (fd_event *ptr);
void client_err(fd_event *ptr);
void client_hup(fd_event *ptr);
void client_new(epoll_manager*em, int fd);

struct timeval now;

void showfd()
{
    system("ls /dev/fd");
}

void tcp_server_in(fd_event* fe)
{
    int fd;
    while (1) {
        fd = accept(fe->fd, 0, 0);
        if (fd < 0) { 
            if (errno != EAGAIN) perror("accept error"); 
            return; 
        }
        log_msg("server [%05d] accept ret: %d", fe->fd, fd);
        client_new(fe->em, fd);
    }
}

int flag_check(void *buf)
{
    uint8_t *c=buf;
    if ((*c == 0xff || *c == 0xfe) && (c[1] == 0 || c[1] == 1))
        return 1;
    else
        return 0;
}

int flag_check_old(void *buf)
{
    if (((unsigned char*)buf)[0] == 0xff &&
            (((unsigned char*)buf)[1] == 0x0 || ((unsigned char*)buf)[1] == 0x1))
        return 1;
    else
        return 0;
}

void rtt_show4(int fd, char *buf, int len, struct sockaddr_in *sa, int socklen)
{
    if (len <=0)
        return;
    if (buf[len-1] == '\n') {
        buf[len-1] = 0;
        log_msg("%lu.%-6lu udp server [%05d] recv %s:%d seq %d bytes %d MSG \e[32m%s\e[m", 
                now.tv_sec, now.tv_usec, fd, 
                inet_ntoa(sa->sin_addr), ntohs(sa->sin_port), 
                *(uint32_t*)(buf+2), len, buf+10);
        buf[len-1] = '\n';
    } else {
        if (len == 1)
            log_msg("%lu.%-6lu udp server [%05d] recv %s:%d seq %d bytes %d HEX \e[32m%02x\e[m", 
                    now.tv_sec, now.tv_usec, fd, 
                    inet_ntoa(sa->sin_addr), ntohs(sa->sin_port), 
                    *(uint32_t*)(buf+2), len, buf[10]);
        else if (len == 2)
            log_msg("%lu.%-6lu udp server [%05d] recv %s:%d seq %d bytes %d HEX \e[32m%02x %02x\e[m", 
                    now.tv_sec, now.tv_usec, fd, 
                    inet_ntoa(sa->sin_addr), ntohs(sa->sin_port), 
                    *(uint32_t*)(buf+2), len, buf[10], buf[11]);
        else if (len == 3)
            log_msg("%lu.%-6lu udp server [%05d] recv %s:%d seq %d bytes %d HEX \e[32m%02x %02x %02x\e[m", 
                    now.tv_sec, now.tv_usec, fd, 
                    inet_ntoa(sa->sin_addr), ntohs(sa->sin_port), 
                    *(uint32_t*)(buf+2), len, buf[10], buf[11], buf[12]);
        else if (len == 4)
            log_msg("%lu.%-6lu udp server [%05d] recv %s:%d seq %d bytes %d HEX \e[32m%02x %02x %02x %02x\e[m", 
                    now.tv_sec, now.tv_usec, fd, 
                    inet_ntoa(sa->sin_addr), ntohs(sa->sin_port), 
                    *(uint32_t*)(buf+2), len, buf[10], buf[11], buf[12], buf[13]);
        else
            log_msg("%lu.%-6lu udp server [%05d] recv %s:%d seq %d bytes %d HEX \e[32m%02x %02x %02x %02x ...\e[m", 
                    now.tv_sec, now.tv_usec, fd, 
                    inet_ntoa(sa->sin_addr), ntohs(sa->sin_port), 
                    *(uint32_t*)(buf+2), len, buf[10], buf[11], buf[12], buf[13]);
    }
}

void general_show4(int fd, char *buf, int len, struct sockaddr_in *sa, int socklen)
{
    int nl;
    if (len <=0)
        return;

    nl = (buf[len-1] == '\n' ? 1 : 0);

    if (nl) {
        buf[len-1] = 0;
        log_msg("%lu.%-6lu udp server [%05d] recv %s:%d bytes %d MSG \e[32m%s\e[m", 
                now.tv_sec, now.tv_usec, fd, 
                inet_ntoa(sa->sin_addr), ntohs(sa->sin_port), 
                len, buf);
        buf[len-1] = '\n';
    } else {
        if (len == 1)
            log_msg("%lu.%-6lu udp server [%05d] recv %s:%d bytes %d HEX \e[32m%02x\e[m", 
                    now.tv_sec, now.tv_usec, fd, 
                    inet_ntoa(sa->sin_addr), ntohs(sa->sin_port), 
                    len, buf[0]);
        else if (len == 2)
            log_msg("%lu.%-6lu udp server [%05d] recv %s:%d bytes %d HEX \e[32m%02x %02x\e[m", 
                    now.tv_sec, now.tv_usec, fd, 
                    inet_ntoa(sa->sin_addr), ntohs(sa->sin_port), 
                    len, buf[0], buf[1]);
        else if (len == 3)
            log_msg("%lu.%-6lu udp server [%05d] recv %s:%d bytes %d HEX \e[32m%02x %02x %02x\e[m", 
                    now.tv_sec, now.tv_usec, fd, 
                    inet_ntoa(sa->sin_addr), ntohs(sa->sin_port), 
                    len, buf[0], buf[1], buf[2]);
        else if (len == 4)
            log_msg("%lu.%-6lu udp server [%05d] recv %s:%d bytes %d HEX \e[32m%02x %02x %02x %02x\e[m", 
                    now.tv_sec, now.tv_usec, fd, 
                    inet_ntoa(sa->sin_addr), ntohs(sa->sin_port), 
                    len, buf[0], buf[1], buf[2], buf[3]);
        else
            log_msg("%lu.%-6lu udp server [%05d] recv %s:%d bytes %d HEX \e[32m%02x %02x %02x %02x ...\e[m", 
                    now.tv_sec, now.tv_usec, fd, 
                    inet_ntoa(sa->sin_addr), ntohs(sa->sin_port), 
                    len, buf[0], buf[1], buf[2], buf[3]);
    }
}

void udp_server_in(fd_event * fe)
{
    struct sockaddr_in addr;
    socklen_t len = sizeof(struct sockaddr_in);
    char buf[1024*10];
    while (1) {
        int ret = recvfrom( fe->fd, buf, sizeof(buf), 0, (struct sockaddr*)&addr, &len);
        if (ret < 0 && errno == EINTR) 
            break;
        gettimeofday(&now, 0);
        if (ret > 0) {
            if (flag_check(buf) == 1) {
                rtt_show4(fe->fd, buf, ret, &addr, len);
            } else {
                general_show4(fe->fd, buf, ret, &addr, len);
            }

            uint8_t *ack  = (uint8_t*)buf;
            uint8_t *type = (uint8_t*)buf+1;
            if (*ack == 0xff && *type == 0x01) {
                -- *ack;
                ret = sendto( fe->fd, buf, ret,0, (struct sockaddr*)&addr, len);
            }
        }
    }
}

void client_new(epoll_manager *em, int fd)
{
    fd_event *pfh = fd_event_new();
    fd_event_init(pfh, em, fd);
    fd_event_set(pfh, EPOLLIN,  client_in);
    fd_event_set(pfh, EPOLLET, 0);
    //fd_event_set(pfh, EPOLLERR, client_err); // 不会被触发
    //fd_event_set(pfh, EPOLLHUP, client_hup); // 不会被触发
    //setsock_rcvtimeo(fd, 5, 0); //非阻塞模式中接收超时事件底层不捕获,
    //仅阻塞模式可获知
    setfd_nonblock(fd);
    //em_fd_event_add(pfh);
    em_fd_event_add(pfh); // 这两种方式都可以
}
void client_out(fd_event *fe)
{
    Em_fd_event_del(fe);
    close(fe->fd);
    if (fe->heap == FD_EVENT_MAGIC_HEAP)
        free(fe);
}
void client_in(fd_event *fe)
{
    int fd = fe->fd;
    char buff[1024] = {0};
    int n;
eintr:
    while ((n = read(fd, buff, sizeof(buff))) > 0) {
        // recv datas ....
        log_msg("%lu.%-6lu tcp client [%05d] read bytes %d %s", now.tv_sec, now.tv_usec, fd, n, buff); 
        send(fd, buff, n, 0);
        //log_msg("%lu.%-6lu tcp client [%05d] echo bytes %d", now.tv_sec, now.tv_usec, fd, n); 
    }
    if (n < 0) {
        if (errno == EAGAIN) return; 
        if (errno == EINTR)  goto eintr; 
        log_msg("client [%05d] read ret %d to error:%s", fd, n, strerror(errno));
        client_out(fe);
    }
    if (n == 0) {
        log_msg("client [%05d] read ret %d to close", fd, n);
        client_out(fe);
    }
}
void client_err(fd_event *fe)
{
    log_msg("client %d client_err", fe->fd);
}
void client_hup(fd_event *fe)
{
    log_msg("client %d client_hup", fe->fd);
}

void _stdin_(fd_event* fe);
void remove_stdin(fd_event_handle fe)
{
    //notify_em_fd_event_release(fe,1);
}

void _stdin_(fd_event* fe)
{
    log_msg("_stdin_");
    int fd = fe->fd;
    char line[1024];
    int n;

eintr:
    while ((n = read(fd, line, sizeof(line))) > 0) {
        write(1, line, n);
        if (strncmp(line, "quit", 4) == 0) exit(0);
        else if (strncmp(line, "exit", 4) == 0) exit(0);
        else if (strncmp(line, "showfd", 6) == 0) showfd();
        else if (strncmp(line, "closeallfd", 10) == 0) close_all_fd();
    }
    if ( n == 0 ) {
        log_msg("stdin Ctrl+D to close."); remove_stdin(fe); return;
    } else {
        if      (errno == EAGAIN) return;
        else if (errno == EINTR ) goto eintr;
        err_msg("stdin error:%s.", strerror(errno));
    }
}

int count;

void _virtual_out_(fd_event *fe)
{
    log_msg("_virtual_out_ %p %d", _virtual_out_, ++count);
}

void before(const epoll_manager * const em)
{
    //log_msg("before");
}
void event(const epoll_manager * const em)
{
    //log_msg("epoll_wait ret %d", em->nfds);
    gettimeofday(&now, NULL);
    if (em->nfds < 0) { 
        if (errno == EAGAIN || errno == EINTR) return;
        err_msg("epoll_wait error %d: %s", errno, strerror(errno)); 
        exit(1); 
    }
}
void after(const epoll_manager * const em)
{
    //log_msg("after");
}


int
main(int argc, char **argv) 
{
    arguments(argc, argv);
    signal(SIGPIPE, SIG_IGN);

    if (daemond) daemon_init(argv[0], 0, 0, 0);  // daemon mode

    //epoll_manager *em = em_open(999, 1000, before, event, after);
    epoll_manager *em = Em_open(999, -1, 0, event, 0);
    Em_run(em);

    // network server tcp
    int tcpsfd = create_tcp_v4_server((char*)"0.0.0.0", port, 1000);
    if (tcpsfd < 0) {
        err_sys("create_tcp_v4_server");
    }
    log_msg("tcp server fd %d, port %d%s", tcpsfd, port, port == 7?" <default>":"");
    fd_event_handle festcp = fd_event_handle_new();
    fd_event_init(festcp, em, tcpsfd);
    fd_event_set(festcp, EPOLLIN, tcp_server_in);
    fd_event_set(festcp, EPOLLET, 0);
    setfd_nonblock(tcpsfd);
    em_fd_event_add(festcp);

    //*
    // network server udp
    int udpsfd = create_udp_v4_server((char*)"0.0.0.0", port, 1000);
    if (udpsfd < 0) {
        err_sys("create_udp_v4_server error");
    }
    log_msg("udp server fd %d, port %d%s", udpsfd, port, port == 7?" <default>":"");
    fd_event* fesudp = fd_event_new();
    fd_event_init(fesudp, em, udpsfd);
    fd_event_set(fesudp, EPOLLIN, udp_server_in);
    fd_event_set(fesudp, EPOLLET, 0);
    em_fd_event_add(fesudp);

    while (1) pause();
    exit(0);

    return 0;
}

